#ifndef CUBE_H
#define CUBE_H

#include <arduino.h>
#include <Spi.h>
#include <string.h>

#define LATCH_DS 11 // spi MOSI
#define LATCH_NC 12 // spi MISO (not connected)
#define LATCH_SHCP 13 //spi SCK
#define LATCH_STCP 10 // spi SS
#define SIZE 8

class Cube {

  private:
    const int BUFFSIZE;
    byte cubeBuffer[SIZE*SIZE/8+SIZE/8];
    byte cube[SIZE][SIZE][SIZE/8];
    
  public:
    const byte SIDESIZE;
    
    Cube()
    :  SIDESIZE(SIZE)
    ,  BUFFSIZE(SIZE*SIZE/8+SIZE/8)
    {
      reset();
    }

    ~Cube(){};
    
    void reset()
    {
      memset(cube, 0, SIZE + SIZE + SIZE/8);
    }
    
    void setPixel(int x, int y, int layer, boolean state)
    {
      int bitPosition=0x7 - (y & 0x7);
      int bytePosition=y >> 3;
      if (state) cube[layer][x][bytePosition]|=(1 << bitPosition);
      else       cube[layer][x][bytePosition]&=0xff ^ (1 << bitPosition);     
    }
    
    void update(byte layer)
    {
        for (int x=0;x<SIDESIZE;x++)
          for (int y=0;y<SIDESIZE/8;y++)
            cubeBuffer[x+y*SIDESIZE]=cube[layer][x][y];
        int layerSelector = 1 << layer;
        cubeBuffer[SIDESIZE*SIDESIZE/8+SIDESIZE/8-1]=(byte)(layerSelector & 0xFF);
        if(SIDESIZE > 8)
          cubeBuffer[SIDESIZE*SIDESIZE/8+SIDESIZE/8]=(byte)(layerSelector >> 8);
        for (int i=0;i<BUFFSIZE;i++) SPI.transfer(cubeBuffer[i]);
        digitalWrite(LATCH_STCP, HIGH);  
        digitalWrite(LATCH_STCP, LOW);
    }
    
    boolean isInCube(int value){
      return value >= 0 && value < SIDESIZE;
    }
    
    boolean isInCube(int x, int y, int z){
      return isInCube(x) && isInCube(y) && isInCube(z);
    }
};


#endif

